﻿using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;

namespace SStuff.FlowControls
{
    public interface IFlowPanel3D
    {
        /// <summary>
        /// Get or set the layout algorithm
        /// </summary>
        IFlowLayout3DAlgorithm LayoutAlgorithm { get; set; }

        /// <summary>
        /// Get or set the Layout offset
        /// </summary>
        double LayoutOffset { get; set; }

        /// <summary>
        /// Get or set the Cyclic mode
        /// </summary>
        bool CyclicMode { get; set; }

        /// <summary>
        /// Get or set the total item count
        /// </summary>
        int ItemCount { get; set; }

        /// <summary>
        /// Get or set the number of items to consider in a layout pass
        /// </summary>
        double ConsideredItems { get; set; }
    }

    /// <summary>
    /// FlowPanel for 3D layouts
    /// </summary>
    public class FlowPanel3D : FlowPanelBase, IFlowPanel3D
    {

        private IFlowLayout3DAlgorithm _layoutAlgorithm;

        /// <summary>
        /// Get or set the layout algorithm
        /// </summary>
        public IFlowLayout3DAlgorithm LayoutAlgorithm
        {
            get { return _layoutAlgorithm; }
            set
            {
                if (_layoutAlgorithm != null)
                    _layoutAlgorithm.LayoutChanged -= OnLayoutAlgorithmLayoutChanged;
                _layoutAlgorithm = value;
                if (_layoutAlgorithm != null)
                    _layoutAlgorithm.LayoutChanged += OnLayoutAlgorithmLayoutChanged;
                InvalidateMeasure();
            }
        }

        void OnLayoutAlgorithmLayoutChanged(object sender, System.EventArgs e)
        {
            InvalidateMeasure();
        }

        private bool _firstAvalableSizeSent = true;
        /// <summary>
        /// Send the size of the viewport to the underlaying layout algorithm
        /// </summary>
        /// <param name="availableSize"></param>
        protected override void SendAvailableSizeToAlgorithm(Size availableSize)
        {

            if (_layoutAlgorithm != null)
            {
                _layoutAlgorithm.SetAvailableSize(availableSize);
#if !SILVERLIGHT
                if(_firstAvalableSizeSent)
                {
                    _firstAvalableSizeSent = false;
                    Dispatcher.BeginInvoke(InvalidateMeasure);
                }
#endif
            }
            //else
            //{
            //    Dispatcher.BeginInvoke(InvalidateMeasure);
            //}
        }
#if !SILVERLIGHT
        protected override void ArrangeChild(Size finalSize, UIElement child)
        {
            base.ArrangeChild(finalSize, child);
            var proj = PlaneProjection.GetPlaneProjection(child as FrameworkElement);
            proj.ViewportSize = finalSize;
            proj.Commit();
        }
#endif


        /// <summary>
        /// Apply the layout algorithm to the specified item
        /// </summary>
        /// <param name="normalizedIndex">index to apply</param>
        /// <param name="element">item to layout</param>
        /// <param name="consideredItemsCount">considered items count to pass to the algorithm</param>
        protected override void ApplyLayout(double normalizedIndex, UIElement element, int consideredItemsCount)
        {
            var item = element as FlowItem3D;
            if (item != null)
            {
                item.NormalizedOffset = normalizedIndex;
                if (_layoutAlgorithm != null)
                {
                    _layoutAlgorithm.ComputeLayout(normalizedIndex, item, consideredItemsCount);
                }
                //else
                //{
                //    Dispatcher.BeginInvoke(InvalidateMeasure);
                //}
            }
        }


        /// <summary>
        /// Apply the Z order to the flow items
        /// </summary>
        /// <param name="children"></param>
        protected override void ApplyZOrder(IEnumerable<UIElement> children)
        {
            int zindex = 1;
            foreach (var child in children.Cast<FlowItem3D>().OrderBy(c => c.Transform3D.GlobalOffsetZ))
            {
                Canvas.SetZIndex(child, zindex++);
            }
        }


        /// <summary>
        /// Get the maximum item count displayed at the same time by the layout algorithm
        /// </summary>
        /// <returns></returns>
        protected override int GetAlgorithmMaxDisplayedItems()
        {
            if (_layoutAlgorithm == null)
                return -1;
            return _layoutAlgorithm.GetMaxDisplayedItems();
        }

    }

    public class NotVirtualizedFlowPanel3D : NotVirtualizedFlowPanelBase, IFlowPanel3D
    {

        private IFlowLayout3DAlgorithm _layoutAlgorithm;

        /// <summary>
        /// Get or set the layout algorithm
        /// </summary>
        public IFlowLayout3DAlgorithm LayoutAlgorithm
        {
            get { return _layoutAlgorithm; }
            set
            {
                if (_layoutAlgorithm != null)
                    _layoutAlgorithm.LayoutChanged -= OnLayoutAlgorithmLayoutChanged;
                _layoutAlgorithm = value;
                if (_layoutAlgorithm != null)
                    _layoutAlgorithm.LayoutChanged += OnLayoutAlgorithmLayoutChanged;
                InvalidateMeasure();
            }
        }

        void OnLayoutAlgorithmLayoutChanged(object sender, System.EventArgs e)
        {
            InvalidateMeasure();
        }

        /// <summary>
        /// Send the size of the viewport to the underlaying layout algorithm
        /// </summary>
        /// <param name="availableSize"></param>
        protected override void SendAvailableSizeToAlgorithm(Size availableSize)
        {

            if (_layoutAlgorithm != null)
                _layoutAlgorithm.SetAvailableSize(availableSize);
            //else
            //{
            //    Dispatcher.BeginInvoke(InvalidateMeasure);
            //}
        }
#if !SILVERLIGHT
        protected override void ArrangeChild(Size finalSize, UIElement child)
        {
            base.ArrangeChild(finalSize, child);
            var proj = PlaneProjection.GetPlaneProjection(child as FrameworkElement);
            proj.ViewportSize = finalSize;
            proj.Commit();
        }
#endif


        /// <summary>
        /// Apply the layout algorithm to the specified item
        /// </summary>
        /// <param name="normalizedIndex">index to apply</param>
        /// <param name="element">item to layout</param>
        /// <param name="consideredItemsCount">considered items count to pass to the algorithm</param>
        protected override void ApplyLayout(double normalizedIndex, UIElement element, int consideredItemsCount)
        {
            var item = element as FlowItem3D;
            if (item != null)
            {
                item.NormalizedOffset = normalizedIndex;
                if (_layoutAlgorithm != null)
                {
                    _layoutAlgorithm.ComputeLayout(normalizedIndex, item, consideredItemsCount);
                }
                //else
                //{
                //    Dispatcher.BeginInvoke(InvalidateMeasure);
                //}
            }
        }


        /// <summary>
        /// Apply the Z order to the flow items
        /// </summary>
        /// <param name="children"></param>
        protected override void ApplyZOrder(IEnumerable<UIElement> children)
        {
            int zindex = 1;
            foreach (var child in children.Cast<FlowItem3D>().OrderBy(c => c.Transform3D.GlobalOffsetZ))
            {
                Canvas.SetZIndex(child, zindex++);
            }
        }


        /// <summary>
        /// Get the maximum item count displayed at the same time by the layout algorithm
        /// </summary>
        /// <returns></returns>
        protected override int GetAlgorithmMaxDisplayedItems()
        {
            if (_layoutAlgorithm == null)
                return -1;
            return _layoutAlgorithm.GetMaxDisplayedItems();
        }

    }
}
